import websocket
import json
import guietta
from guietta import Gui,_,___,III,QLabel
from PyQt5 import QtCore
from PyQt5.QtWidgets import QTextEdit
from threading import Thread
from collections import namedtuple

##### main codes
class _:
	'''Empty.You can use it as a unknown arg'''
	def __init__(self,*args):
		pass

	@ staticmethod
	def __repr__():
		return "< Empty >"


class FoolishConfig:
	def __init__(self,ws_address):
		'''
		:*ws_address : the wss/ws's address

		: on_message : when receiving data
		: on_error   : when error is appearing
		: on_close   : when the program is closed
		: on_open    : when being connected to the server 
		: sendmsg    : when click button
		-----
		use "_" as a unknown or empty thing
		'''
		self.ws_address = ws_address

		self.on_message = _ 
		self.on_error = _ 
		self.on_close = _ 
		self.on_open = _ 
		self.sendmsg = _

	def __repr__(self):
		local = ["ws_address","on_message","on_error","on_close","on_open","sendmsg"]
		extend = dict([(key,value) for key,value in vars(self).items() if key not in local])
		extend_string = list(["			{: <15}: {}\n".format(key,value) for key,value in extend.items()])
		information = f'''
			config:
			basic information -----
			ws_address     : {self.ws_address.__repr__()}

			web information -------
			on_message     : {self.on_message.__repr__()}
			on_error       : {self.on_error.__repr__()}
			on_close       : {self.on_close.__repr__()}
			on_open        : {self.on_open.__repr__()}

			gui information -------
			sendmsg        : {self.sendmsg.__repr__()}

			extended information --\n{ "".join(extend_string) }
 			'''
		return information


class FoolishCore:
	def __init__(self,ws_address):
		'''
		: ws_address : for FoolishConfig's arg 'ws_address'
		'''
		self.config = FoolishConfig(
			ws_address = ws_address
			)

	def init_gui(self):
		message_text = QTextEdit()
		message_text.setReadOnly(True)
		gui = Gui(
			[(message_text,"message_text"),___],
			["__send_text__",["send_button"]])

		gui.events(
			[("textChanged",_),("textChanged",_)],[("returnPressed",self.sendmsg),self.sendmsg])
		# guietta.connect(send_button)


		gui.window().setGeometry( 100,100,800,500 )

		return gui

	def ws_connect(self):
		websocket.enableTrace(True)
		self.ws = websocket.WebSocketApp(
			self.config.ws_address,
			on_message = self.config.on_message,
			on_error = self.config.on_error,
			on_close = self.config.on_close
			)
		self.ws.on_open = self.config.on_open	
		self.ws.run_forever()

	def setup(self,gui=False):
		'''
		connect to the server
		'''
		if gui:
			self.gui = self.init_gui()
			ws_thread = Thread(target=self.ws_connect)

			ws_thread.start()
			self.gui.run()
		else:
			self.ws_connect()


##### extended codes
class HackChatBot(FoolishCore):
	def __init__(self,name,channel,password=None):
		FoolishCore.__init__(
			self = self,
			ws_address = "wss://hack.chat/chat-ws"
			)

		self.config.on_message = self.on_message
		self.config.on_error = self.on_error
		self.config.on_close = self.on_close
		self.config.on_open = self.on_open

		# extended config
		self.config.name = name 
		self.config.password = password
		self.config.channel = channel

		self.config.data_join = self.data_join
		self.config.data_text = self.data_text
		self.config.data_emote = self.data_emote
		self.config.data_changenick = self.data_changenick
		self.config.data_changecolor = self.data_changecolor

	def sendmsg(self,gui,*args):
		message = gui.widgets["send_text"].text()
		if message != "":
			self.gui.widgets["send_text"].clear()
			self.ws.send(self.data_text(message))

	def on_message(self,ws,message):
		'''
		receive data
		'''
		print(f"[INFO] RECEIVE: {message}")
		data = json.loads(message)

	def on_error(self,ws,error):
		'''
		error
		'''
		print(f"[ERROR] {error}")

	def on_close(self,ws):
		'''
		exit
		'''
		print(f"[INFO] EXIT")

	def on_open(self,ws):
		'''
		when connecting the server
		'''
		if self.config.password == None:
			ws.send(self.data_join(channel=self.config.channel,nick=self.config.name))
		else:
			ws.send(self.data_join(channel=self.config.channel,nick=f"{self.config.name} #{self.config.password}"))

	@ staticmethod
	def data_join(channel,nick):
		'''
		join the channel with nickname
		'''
		return json.dumps({"cmd":"join","channel":channel,"nick":nick})

	@ staticmethod
	def data_text(text):
		'''
		chat message
		'''
		return json.dumps({"cmd":"chat","text":text}) 

	@ staticmethod
	def data_emote(text):
		'''
		eq /me
		'''
		return json.dumps({"cmd":"emote","text":text})

	@ staticmethod
	def data_changenick(nick):
		'''
		change the nickname
		'''
		return json.dumps({"cmd":"changenick","nick":nick})

	@ staticmethod
	def data_changecolor(code):
		'''
		change the color
		'''
		return json.dumps({"cmd":"changecolor","color":code})


class XChatBot(FoolishCore):
	def __init__(self,nick,password,channel):
		FoolishCore.__init__(self=self,ws_address="wss://xq.kzw.ink/ws")

		self.config.on_message = self.on_message
		self.config.on_error = self.on_error
		self.config.on_close = self.on_close
		self.config.on_open = self.on_open

		# extended config
		self.config.nick = nick
		self.config.password = password
		self.config.channel = channel

	def on_message(self,ws,message):
		print(f"Get:{message}")
		pass

	def on_error(self,ws,error):
		print(error)
		pass

	def on_close(self,ws):
		print("### closed ###")
		pass

	def on_open(self,ws):
		# connect
		ws.send(self.data_join(self.config.nick,self.config.password,self.config.channel))
		ws.send(self.data_get_old(num=10))

	@ staticmethod
	def data_join(nick,password,channel):
		return json.dumps({
			"channel":channel,
			"nick":f"{nick} #{password}",
			"cmd":"join",
			"show":0,
			"head":"d"})

	@ staticmethod
	def data_chat(text):
		return json.dumps({
			"cmd":"chat",
			"head":"d",
			"show":0,
			"text":text
			})

	@ staticmethod
	def data_get_old(num):
		# num is a number !
		return json.dumps({"cmd":"get_old","num":num})


if "tourist" == "tourist":
	##### introduction codes
	'''
	FoolishConfig(ws_address) 
	## read the codes of 'FoolishConfig',it's really useful
	# ws_address # the ws/wss's address
	# on_message # when receiving data
	# on_error   # when error is appearing
	# on_close   # when the program is closed
	# on_open    # when being connected to the server 

	FoolishCore(ws_address)
	<property>   # config # I rewrite a beautiful __repr__,it can tell you the bot's config
	<method  >   # setup  # start the bot
	'''


	##### example codes

	##################################################################
	'''
	Make a common hackchatbot,easy example

	foolishbot = HackChatBot(name="foolishbot",channel="your-channel")
	print(foolishbot.config)  # <--this can read the bot's config
	foolishbot.setup()
	'''
	##################################################################

	##################################################################
	'''
	Make a better hackchatbot

	# In fact, the function 'on_message' is included in the HackChatBot
	# but you can change the default 'on_message' by setting 'self.config'
	def on_message(ws,message):
		print(message)
		ws.send(json.dumps({"cmd":"chat","text":"hey"}))
		# this is only an example,if you really use this
		# it will lead a disaster

	foolishbot = HackChatBot(name="foolishbot",channel="your-channel")
	foolishbot.config.on_message = on_message
	print(foolishbot.config)
	foolishbot.setup()
	'''
	##################################################################

	##################################################################
	'''
	Better and Better

	...
	'''
	##################################################################
	pass 